package kayao.client.scene;

import java.util.Vector;

import com.chopsticks3d.math.FastMath;
import com.chopsticks3d.scene.SceneNode;

import kayao.client.data.MissionHandler;
import kayao.client.data.SharedMemory;
import kayao.client.data.Subscriber;
import kayao.client.network.message.MessageHandler;
import kayao.client.network.message.StateUpdateMessage;
import kayao.client.scene.objects.CarCamera;
import kayao.client.scene.objects.CarNode;
import kayao.client.scene.objects.Entity;
import kayao.client.scene.objects.PhysicalNode;
import kayao.common.KayaoValues;

import android.os.SystemClock;

/**
 * The modellingthread of yantag, handling reapting updates of the scene
 * such as dead reckoning.
 */
public class ModellingThread extends Thread {
	private long mLastTimeUpdate;
	private long mTime;
	private long mTimeDelta;
	private float mTimeDeltaSeconds;
	private boolean mRunning = true;
	private boolean mDead = false;
	
	private PhysicalNode mPhysNode = new PhysicalNode();

	private long mLastAreaUpdate = 0;
	private long mLastStateUpdate = 0;

	public boolean terminate(){
		mRunning = false;
		while(!mDead){
			synchronized(this){
				this.notify();
			}
			System.out.println("STUCK IN MODELLING");
			Thread.yield();
		}
		return false;
	}


	@Override
	public void run() {
		PhysicsController.createWorld(-KayaoValues.TOTAL_MAP_WIDTH / 2,
				-KayaoValues.TOTAL_MAP_HEIGHT / 2,
				KayaoValues.TOTAL_MAP_WIDTH / 2,
				KayaoValues.TOTAL_MAP_HEIGHT / 2);

		System.out.println("### ModellingThread started");

		while(mRunning) {
			synchronized(this) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			// Calculation of delta time
			mTime = SystemClock.uptimeMillis();
			mTimeDelta = mTime - mLastTimeUpdate;
			mTimeDeltaSeconds = mLastTimeUpdate > 0.0f ? mTimeDelta / 1000.0f : 0.0f;
			mLastTimeUpdate = mTime;

			Vector<Entity> entities = SharedMemory.getInstance().getEntitiesAsVector();

			for (int i = 0; i < entities.size(); i++) {
				Entity e = entities.get(i);
				if (e instanceof CarNode) {					
					synchronized(e) {
						doConvergence((CarNode)e);
						if (e.getBodyId() != -1) {
							PhysicsController.setCarValues(e.getBodyId(),
									e.getTranslationX(),
									e.getTranslationZ(),
									-e.getRotationY() * FastMath.DEG_TO_RAD,
									((CarNode)e).deltaRotation * FastMath.DEG_TO_RAD,
									((CarNode)e).getVelocityX(),
									((CarNode)e).getVelocityZ());
						}
					}
				}
			}

			CarNode node = SharedMemory.getInstance().getAvatar();
			synchronized(node) {
				if (node.getBodyId() != -1) {
					PhysicsController.setCarValues(node.getBodyId(),
							node.getTranslationX(),
							node.getTranslationZ(),
							-node.getRotationY() * FastMath.DEG_TO_RAD,
							node.deltaRotation * FastMath.DEG_TO_RAD,
							node.getVelocityX(),
							node.getVelocityZ());
				}
			}

			// Step forward in the physics
			PhysicsController.step(mTimeDeltaSeconds);

			// Update the scene according to the physics
			SceneNode scene = SharedMemory.getInstance().getScene();
			synchronized(scene) {
				scene.update();
				node.updateAvatarTransformation();
			}

			// Update camera
			CarCamera camera = SharedMemory.getInstance().getCamera();
			synchronized(camera) {
				camera.update(mTimeDeltaSeconds);
			}
			
			float dx = Math.abs(node.getTranslationX() - MissionHandler.getFlagPosX());
			float dz = Math.abs(node.getTranslationZ() - MissionHandler.getFlagPosZ());
			if (Math.sqrt(dx*dx + dz*dz) < 1) {
				if (!MissionHandler.hasFlag()) {
					MissionHandler.tookFlag();
				}
			}
			
			if(mLastAreaUpdate < mTime) {
				node = SharedMemory.getInstance().getAvatar();
				synchronized(node) {
					AreaManager.interesstingRegionsByCord(node.getTranslationX() + KayaoValues.TOTAL_MAP_WIDTH / 2,
							node.getTranslationZ() + KayaoValues.TOTAL_MAP_HEIGHT / 2);
				}

				mLastAreaUpdate = SystemClock.uptimeMillis() + 1000;
			}

			if(mLastStateUpdate < mTime) {
				for(Subscriber s: AoIManager.getSubscribers()){
					MessageHandler.insertOutgoingMessage(new StateUpdateMessage(
							SharedMemory.getInstance().getClientById(s.getId()).getIP()));
				}

				mLastStateUpdate = SystemClock.uptimeMillis() + 500;
			}
		}
		mDead = true;
	}

	private void doConvergence(CarNode car) {
		if (KayaoValues.USE_CONVERGENCE) {
			
			if (car.gotNewState()) {
				System.out.println("Doing dead reckoning...");
				System.out.println("Ghost x: " + car.getGhostTranslationX());
				PhysicsController.performDeadReckoning(mPhysNode,
						car.getGhostTranslationX(),
						car.getGhostTranslationZ(),
						-car.getGhostRotationY() * FastMath.DEG_TO_RAD,
						0, // TODO: ghost and sending angular velocity
						car.getGhostVelocityX(),
						car.getGhostVelocityZ(),
						2.0f);
				System.out.println("Phys x: " + mPhysNode.getTranslationX());
				car.setNewState(false);
				car.setGhostTranslation(mPhysNode.getTranslationX(), 0, mPhysNode.getTranslationZ());
				car.setGhostRotationY(mPhysNode.getRotationY());
				car.setTranslation(car.getGhostTranslationX(), 0, car.getGhostTranslationZ());
			}
			
			return;
		}
		if (car.gotNewState()) {
			car.setTranslation(car.getGhostTranslationX(), 0, car.getGhostTranslationZ());
			car.setRotationY(car.getGhostRotationY());
			car.setVelocityX(car.getGhostVelocityX());
			car.setVelocityZ(car.getGhostVelocityZ());
			car.deltaRotation = car.getGhostAngularVelocity();
			car.setNewState(false);
		}
	}
}